-
Notifications
You must be signed in to change notification settings - Fork 103
[sql-33] session: add migration code from kvdb to SQL #1051
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
[sql-33] session: add migration code from kvdb to SQL #1051
Conversation
449a2cc
to
1828ec4
Compare
1828ec4
to
a868a93
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding some review comments where input would be extra helpful :)!
macRecipe = &MacaroonRecipe{} | ||
if perms != nil { | ||
macRecipe.Permissions = unmarshalMacPerms(perms) | ||
} | ||
if caveats != nil { | ||
macRecipe.Caveats = unmarshalMacCaveats(caveats) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know if you think we should rather fix the kvdb store implementation rather than the sql one. But as we've previously opted for not updating the store behaviour, and make the sql one mimic the behaviour until we've completed the migration, I opted for this version here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, I could also override this in newly added overrideMacaroonRecipe
function before the migration deep equals check. But I kept this as is, as I do think it's correct behaviour that we don't set the Permissions
or Caveats
and keep them at nil
, if no value actually exists for them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think in this case it is fine to fix on the kvdb side so that our sql side can stay neat?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also commit message says fix kvdb&sql but this only touches sql
session/sql_migration.go
Outdated
if !session.RevokedAt.IsZero() { | ||
err = tx.SetSessionRevokedAt( | ||
ctx, | ||
makeSetRevokedAtParams(sqlId, session.RevokedAt), | ||
) | ||
if err != nil { | ||
return err | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could potentially update the insert session sql query to also take an optional RevokedAt sql.NullTime
param, to not have to do this as a separate step. I opted for this version to keep things as is as much as possible, but if you think that'd be better, I'll update to do that instead :)
session/sql_migration_test.go
Outdated
{ | ||
"randomized sessions", | ||
randomizedSessions, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Opted for a test that uses the "standard" rand
lib here, as @bitromortac had a preference for that in #1047 (comment). But can also look into adding a test that uses rapid
if you'd prefer.
// We always have a caveat.Id, but the rest are | ||
// randomized if they exist or not. | ||
macCaveat.Id = randomBytes(rand.Intn(10) + 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know if this is not true in for real world data, as migrations WILL fail if we don't set a caveat.Id
when the caveats
param of WithMacaroonRecipe
is not nil
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could potentially override this as well in the new overrideMacaroonRecipe
as well. But I do not think this scenario can happen for real world data. But open to implementing that as well if you think it's worth doing it just to be sure.
session/sql_migration_test.go
Outdated
// "dest" state, without checking if the state transition is legal. | ||
// | ||
// NOTE: this function should only be used for testing purposes. | ||
func (db *BoltStore) shiftStateUnsafe(_ context.Context, id ID, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not fully sure if we add test functions like this one that's a struct
member to the _test
file, or if we add it in the actual struct file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right, not too great, you could change to func shiftStateUnsafe(_ context.Context, db *BoltStore, id ID, dest State) error {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work, very clean on first pass! 🔥
session/sql_migration.go
Outdated
// The remote public key is currently only set for autopilot sessions, | ||
// else it's an empty byte array. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I think it should read else it's a nil byte array
, since we compare to nil in the code in other sites, which is also correctly set above with the var
statement
session/sql_migration_test.go
Outdated
require.NoError(t, err) | ||
|
||
t.Cleanup(func() { | ||
require.NoError(t, kvStore.DB.Close()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tiny nit: the embedded DB can be removed
session/sql_migration_test.go
Outdated
// numberOfSessions is set to 100 to add enough sessions to get | ||
// enough variation between number of invoices and payments, but | ||
// kept low enough for the test not take too long to run, as the | ||
// test time increases drastically by the number of sessions we | ||
// migrate. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the doc mentions invoice and payments, which is probably a copy/paste
session/sql_migration_test.go
Outdated
// "dest" state, without checking if the state transition is legal. | ||
// | ||
// NOTE: this function should only be used for testing purposes. | ||
func (db *BoltStore) shiftStateUnsafe(_ context.Context, id ID, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right, not too great, you could change to func shiftStateUnsafe(_ context.Context, db *BoltStore, id ID, dest State) error {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
holding off on review here until the prep PR discussed in the accounts mig PR is in (since that will be used to test this one)
Now that #1047 has been merged, I'll update this PR to adhere to the feedback brought up in that PR, and then re-request reviews afterwards. |
In preparation for upcoming migration tests from a kvdb to an SQL store, this commit updates the NewTestDB function to return the Store interface rather than a concrete store implementation. This change ensures that migration tests can call NewTestDB under any build tag while receiving a consistent return type.
@ViktorTigerstrom, remember to re-request review from reviewers when ready |
When a session has a MacaroonRecipe set, which has a nil value set for either the `Permissions` or the `Caveats` field, the kvdb session store would return that field as nil, while the sql prior to this commit would return an empty array. When the we migrate the session store from kvdb to sql, that will cause the kvdb and the migrate session store to differ when comparing them, which will cause the migration to fail. This commit fixes that by ensuring that the sql session store returns a nil value for those fields in that scenario, rather than an empty array.
a868a93
to
4b3b39f
Compare
Updated the PR to also address feedback raised in #1047! Will address the small linting issue after next review round :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, utACK 🎉
session/sql_migration.go
Outdated
}, | ||
} | ||
|
||
err = tx.InsertSessionMacaroonCaveat( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: fits on single line, same for InsertSessionPrivacyFlagParams
, I found this in other places as well, where you could contract a bit more
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I addressed that + other places that I found, so I hope this has been addressed now.
a48e9b2
to
8c970bf
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for the review @bitromortac 🎉!! I noticed that we can have real world data that sets a non-nil macaroon recipe, but with nil
perms
& caveats
. I updated the code to be able to handle that case as well.
}, | ||
}, | ||
{ | ||
name: "macaroon recipe with nil perms and caveats", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this test is new, as this can actually happen for real world sessions.
macRecipe = &MacaroonRecipe{} | ||
if perms != nil { | ||
macRecipe.Permissions = unmarshalMacPerms(perms) | ||
} | ||
if caveats != nil { | ||
macRecipe.Caveats = unmarshalMacCaveats(caveats) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, I could also override this in newly added overrideMacaroonRecipe
function before the migration deep equals check. But I kept this as is, as I do think it's correct behaviour that we don't set the Permissions
or Caveats
and keep them at nil
, if no value actually exists for them.
// We always have a caveat.Id, but the rest are | ||
// randomized if they exist or not. | ||
macCaveat.Id = randomBytes(rand.Intn(10) + 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could potentially override this as well in the new overrideMacaroonRecipe
as well. But I do not think this scenario can happen for real world data. But open to implementing that as well if you think it's worth doing it just to be sure.
This commit introduces the migration logic for transitioning the sessions store from kvdb to SQL. Note that as of this commit, the migration is not yet triggered by any production code, i.e. only tests execute the migration logic.
8c970bf
to
46873cb
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looking good!
macRecipe = &MacaroonRecipe{} | ||
if perms != nil { | ||
macRecipe.Permissions = unmarshalMacPerms(perms) | ||
} | ||
if caveats != nil { | ||
macRecipe.Caveats = unmarshalMacCaveats(caveats) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think in this case it is fine to fix on the kvdb side so that our sql side can stay neat?
macRecipe = &MacaroonRecipe{} | ||
if perms != nil { | ||
macRecipe.Permissions = unmarshalMacPerms(perms) | ||
} | ||
if caveats != nil { | ||
macRecipe.Caveats = unmarshalMacCaveats(caveats) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also commit message says fix kvdb&sql but this only touches sql
// If sessions are linked to a group, we must insert the initial session | ||
// of each group before the other sessions in that group. This ensures | ||
// we can retrieve the SQL group ID when inserting the remaining | ||
// sessions. Therefore, we first insert all initial group sessions, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interesting - are we not able to rely on the fact that sessions are stored in order in kvdb? ie, if we get a session with a group ID set, that will always either point to the session at hand or a session we've already handled?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ie, i think we can just:
for s := sessions {
if s.groupID = s.ID {
<insert new session. no coupled group>
continue
}
groupID := db.GetGroupIDByAlias(s.groupID)
<insert new session. couple to group>
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or did you actually run into a scenario where these were not in order?
session.AccountID.WhenSome(func(alias accounts.AccountID) { | ||
// Check that the account exists in the SQL store, before |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
technically what we are doing here is just getting the account DB level ID.
we dont actually need to "check before link" since SQL does that for us.
return | ||
} | ||
|
||
acctID = sql.NullInt64{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sqldb.SQLInt64(accDBID)
if !session.RevokedAt.IsZero() { | ||
setSessionRevokedParams := sqlc.SetSessionRevokedAtParams{ | ||
ID: sqlId, | ||
RevokedAt: sql.NullTime{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sqldb.SQLTime
// Since the InsertSession query doesn't support that we set the revoked | ||
// field during the insert, we need to set the field after the session | ||
// has been created. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
// session itself, and therefore the SQL id for the session won't exist | ||
// prior to inserting the session. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
// Now lets set the group ID for the session. | ||
err = tx.SetSessionGroupID(ctx, sqlc.SetSessionGroupIDParams{ | ||
ID: sqlId, | ||
GroupID: sql.NullInt64{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sqldb.SQLInt64
privacyFlagParams := sqlc.InsertSessionPrivacyFlagParams{ | ||
SessionID: sqlId, | ||
Flag: int32(privacyFlag), | ||
} | ||
|
||
err = tx.InsertSessionPrivacyFlag(ctx, privacyFlagParams) | ||
if err != nil { | ||
return err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this and many of the above can just be in-lined instead of having separate param vars
This PR introduces the migration logic for transitioning the sessions store from kvdb to SQL.
Note that as of this PR, the migration is not yet triggered by any production code, i.e. only tests execute the migration logic.
Part of #917